package com.yyaccp.easybuy.security.config;

import com.yyaccp.easybuy.security.auth.NoPasswordTokenProvider;
import com.yyaccp.easybuy.security.service.UserDetailsServiceImpl;
import com.yyaccp.easybuy.security.service.token.TokenService;
import com.yyaccp.easybuy.security.util.JsonUtil;
import com.yyaccp.easybuy.security.vo.AuthProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthProperties authProperties;
    @Resource
    private TokenFilter tokenFilter;
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Resource
    private TokenService tokenService;
    @Autowired
    private NoPasswordTokenProvider noPasswordTokenProvider;

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        // 验证码登录，sns登录等
        auth.authenticationProvider(noPasswordTokenProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        log.info("鉴权基本配置：{}", authProperties);
        // 禁用csrf，简化登录表单的参数传递
        http.csrf().disable();
        // 禁用httpSession
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 授权配置
        if ("url".equals(authProperties.getMode())) {
            http.authorizeRequests()
                    // 可以匿名访问的控制器
                    .antMatchers(authProperties.getExcludedList().split(",")).anonymous()
                    // 基于url鉴权
                    .antMatchers("/**").access("@rbacService.hasPerms(request, authentication)")
                    // 除上面以外，其他控制器都需要登录后访问
                    .anyRequest().authenticated();
        } else {
            http.authorizeRequests()
                    // 可以匿名访问的控制器
                    .antMatchers(authProperties.getExcludedList().split(",")).anonymous()
                    // 除上面以外，其他控制器都需要登录后访问，需要在控制器的方法上使用 @PreAuthorize鉴权
                    .anyRequest().authenticated();
        }
        // 异常处理
        http.exceptionHandling()
                // 登录验证失败处理
                .authenticationEntryPoint((request, response, e) -> {
                    log.info(e.getMessage(), e);
                    JsonUtil.error(response, HttpStatus.UNAUTHORIZED.value(), "未登录。。。");
                })
                // 权限不足异常处理
                .accessDeniedHandler((request, response, e) -> {
                    log.info(e.getMessage(), e);
                    JsonUtil.error(response, HttpStatus.FORBIDDEN.value(), "未授权。。。");
                });
        // 注销配置，默认路径（/logout)
        http.logout().logoutUrl("/logout").logoutSuccessHandler(((request, response, authentication) -> {
            tokenService.deleteToken(request.getHeader("token"));
            JsonUtil.ok(response, "注销成功");
        }));
        // 鉴权前先执行自定义的token过滤器设置登录的用户信息
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
